﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using WCFServiceWebRole1;
using ChatData;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using System.Linq;
////using ClientServerChat;

namespace WCFServiceWebRole1
{
    public static partial class Extensions
    {
        public static Post ConverttoPost(this Post post, StreamsDataModel item)
        {
            post.IP = item.IP;
            post.MessageContent = item.Message;
            post.Time = item.Time;
            post.User = item.UserName;

            return post;
        }
        public static DMPost ConverttoDMPost(this DMPost post, DMDataModel item)
        {
            post.IP = item.IP;
            post.MessageContent = item.Message;
            post.Origin = item.Origin;
            post.Time = item.Time;
            post.User = item.UserName;

            return post;
        }
    }
    /// <summary>
    /// Handles Client Requests
    /// </summary>
    public class TheServer
    {
        public CloudStorageAccount acc=CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
        CloudBlobClient blobclient;
        ActiveUserStore<Record> activeusers = new ActiveUserStore<Record>();
        UsersDataSource users;
        StreamsDataSource thestream;
        DMDataSource DMstream;
        FilesDataSource fileStore;

        public TheServer(CloudStorageAccount account)
        {
            acc = account;
            blobclient = acc.CreateCloudBlobClient();
            users = new UsersDataSource(acc);
            thestream = new StreamsDataSource(acc);
            DMstream = new DMDataSource(acc);
            fileStore = new FilesDataSource(acc);

        }
        //http://msdn.microsoft.com/en-us/library/system.io.stream.read.aspx
        public List<Record> HandleUserRequest()
        {
            lock (activeusers)
            {
                return this.activeusers.ToList();
            }
        }
        public FilePost HandleFileRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (activeusers)
            {
                if (!activeusers.isLoggedIn(username)) return null;
            }
            lock (fileStore)//obtain lock on filestore before proceeding
            {
                switch (therequest.Type)
                {
                    case RequestType.GetFile:
                        //chatserver reads in the filebytes before sending it
                        // it does not remove the entry from memory
                        //File reversed inn chatserver.svc
                        FilePost newfile = new FilePost();
                        var result =fileStore.getByGUID(therequest.GUID);
                        newfile.LocalFile = result.BlobRef;
                        newfile.Time = result.Time;
                        newfile.User = result.User;
                        newfile.GUID = result.GUID;
                        newfile.File=blobclient.GetBlobReference(result.BlobRef).DownloadByteArray();

                        return newfile;
                    //file
                    case RequestType.File:
                            FilesDataModel item = new FilesDataModel();
                            item.GUID = Guid.NewGuid().ToString();
                            item.BlobRef = Utils.EncodePasswordToBase64(therequest.FileName);
                            item.User = username;
                            item.Time = DateTime.Now;
                            fileStore.Insert(item);
                            blobclient.GetBlobReference(item.BlobRef).UploadByteArray(therequest.File);
                            Reply.SendNotificationFile(activeusers.getIP(therequest.Destination), activeusers.getPort(therequest.Destination), item.GUID);
                        break;
                    case RequestType.FowardFile:
                        Reply.SendNotificationFile(activeusers.getIP(therequest.Destination), activeusers.getPort(therequest.Destination), therequest.GUID);
                        return null;
                        break;
                }
            }
            return null;
        }
        public List<DMPost> HandleDMStreamRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (activeusers)
            {
                if (!activeusers.isLoggedIn(username)) return null;
            }
            lock (DMstream)
            {
                switch (therequest.Type)
                {
                    case RequestType.GetDMStream:
                        var result = from c in DMstream.getUserStream(username)
                                     select new DMPost().ConverttoDMPost(c);                            
                            return result.ToList();
                        break;
                    case RequestType.GetDMStreamSince:
                        var resultsince = from c in DMstream.getUserStream(username)
                                          select new DMPost().ConverttoDMPost(c);                      
                            return resultsince.ToList();
                        break;
                }
            }
            return null;

        }
        public List<Post> HandleStreamRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (thestream)
            {
                switch (therequest.Type)
                {
                    case RequestType.GetStream:
                        if (activeusers.isLoggedIn(username))
                        {
                            var result = from c in thestream.SelectStream()
                                         select new Post().ConverttoPost(c);
                            
                            return result.ToList();
                        }
                        break;
                    case RequestType.GetStreamSince:
                        if (activeusers.isLoggedIn(username))
                        {
                            var resultsince = from c in thestream.getStreamSince(therequest.Time)
                                              select new Post().ConverttoPost(c);
                           
                            return resultsince.ToList();
                        }
                        break;
                }

                return null;
            }
        }
        public bool HandleRequest(Request therequest)
        {
            string username = therequest.Username;
            lock (activeusers)
            {
                if (therequest.Type == RequestType.Login)
                {
                    if (users.Login(username, therequest.Password))// check that the username and password match
                    {
                        if (!activeusers.isLoggedIn(username))// check if they are already logged on
                        {
                            Reply.NotifyAllLogIn(activeusers, therequest.Username);// user will not have a TCP listner running yet so add after to avoid exception being thrown when trying to contact user
                            activeusers.Add(username, therequest.IP, therequest.Port, therequest.Status);
                            return true;
                        }
                        else return false;
                    }
                    else return false;
                }
                if (!activeusers.isLoggedIn(username)) return false;
                switch (therequest.Type)
                {
                    //message
                    case RequestType.Message:
                            StreamsDataModel thepost = new StreamsDataModel();
                            thepost.UserName = username;
                            thepost.IP = therequest.IP;
                            thepost.Message = therequest.Mesage;
                            thepost.Time = DateTime.Now;
                            lock(thestream)
                            {
                                thestream.Insert(thepost);
                            }
                        break;
                    //DM
                    case RequestType.DM:
                        DMDataModel thedm = new DMDataModel();
                        thedm.Origin = username;
                        thedm.UserName = therequest.Destination;
                        thedm.Message = therequest.Mesage;
                        thedm.IP = therequest.IP;
                        thedm.Time = therequest.Time;
                        lock (DMstream)
                        {
                            DMstream.Insert(thedm);
                        }
                        if (activeusers.getStatus(therequest.Destination) == "Avaliable")
                        {
                            Reply.SendNotificationDM(activeusers.getIP(therequest.Destination), activeusers.getPort(therequest.Destination), username);
                        }
                        break;
                    //logoff
                    case RequestType.Logoff:
                        //update a central list/ ping connected clients to refresh
                        users.Update(username, activeusers.getIP(username),activeusers.getPort(username), activeusers.getStatus(username));
                            activeusers.Remove(username);
                                Reply.NotifyAllLogOff(activeusers, username);
                        break;
                    //status update
                    case RequestType.Status:
                            activeusers.UpdateStatus(username, therequest.Status);
                               Reply.NotifyAllStatus(activeusers, username);
                        break;
                }
                return true;
            }
        }
    }
}
